/*
 * 通过密钥文件得到其MD5值//同理可以使用其他类型的hash值
 * 将MD5值转换为一个长度为16的byte数组
 * 将byte数组的所有值都转换为正数相加得到和sum
 * 输入流一次读取sum个字节
 * 将sum个字节分为16组
 * 每组字节数对应为byte数组每个元素正数数量的字节
 * 对每组字节进行异或运算
 * 
 * 用来做异或运算的数是对0xf进行操作所得
 * i=sum%4，(0xf>>i)<<i，即可得到1111，1110，1100，1000
 * 再得到11110000 11110000，11100000 11100000，11000000 11000000，10000000 10000000
 * 对每组字节数进行异或操作后，上述四个数右移一位，即每8组所做的异或操作相同
 * 
 * 加密完成后提示是否删除原文件
 * 文件长度小于sum时，并未加密
 * 由密钥文件或密码决定使用哪类hash值，只使用hash值的前16个字节
 * 
 * 增强密码强度，右移再移回来的位数使用别的hash值（是别的文件或密码的hash值）求得，可以用来实现多条件加密
 * 多个密码或密钥文件
 * 128*16=2048，一次读取的字节数最多2K，用int移位32次最多4K，用long可以达到8K，用来提高效率
 * 名字的md5的md5做密码
 * 两个8循环可以使用不同的异或方式
 * 对于最后一输出也进行加密
 * 将名字写到加密后的文件中，可以将一部分的md5值也写进去，忘了密码可以试个几百次就成功了
 */

package myEncriptor;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.swing.JOptionPane;

/**
 * 
 * @author heantai
 */
class MyEncriptor {
	public boolean encripted = false;
	public boolean encripting = false;
	public static MyEncriptor myEncriptor = new MyEncriptor();

	private MyEncriptor() {

	}

	public static MyEncriptor instance() {
		return myEncriptor;
	}

	void encript(File keyFile, File tobeEncripted, File saved)
			throws FileNotFoundException, IOException, NoSuchAlgorithmException {
		// 空文件的md5值，两个内容完全相同的文件的md5值，如果相同就不需要密鉏文件了，随时新建一个就可以解密
		FileInputStream in = new FileInputStream(keyFile);
		byte[] buf = new byte[1024];
		MessageDigest md5 = MessageDigest.getInstance("md5");
		int numRead;
		while ((numRead = in.read(buf)) != -1) {
			md5.update(buf, 0, numRead);
		}
		in.close();
		byte[] bs = md5.digest();
		int sum = 0;
		int[] len = new int[16];
		for (int i = 0; i < bs.length; i++) {
			sum += bs[i] & 0xff;
			len[i] = bs[i] & 0xff;
		}
		in = new FileInputStream(tobeEncripted);
		buf = new byte[sum];
		FileOutputStream out = new FileOutputStream(saved);
		byte[] bufout = new byte[sum];
		int num;

		// 修改为一次操作获得掩码mask
		// 掩码中一的个数决定sum对几取余
		int yushu = sum % 4;
		int yiweihou = (0xf >> yushu) << yushu;// 1111，1110，1100，1000
		int yihoulai = yiweihou << 4 | yiweihou << 12;// 11110000
														// 11110000，11100000
														// 11100000，11000000
														// 11000000，10000000
														// 10000000

		while ((num = in.read(buf)) == sum) {
			int leng = 0;
			int length = len[0];
			int lenCount = 0;
			int yudeshu = yihoulai;
			for (int j = 0; j < 16; j++) {
				for (int i = leng; i < length; i++) {
					bufout[i] = (byte) (buf[i] ^ yudeshu);
				}
				leng = length;
				if (lenCount < 15) {// 防止l++enCount为16造成数组越界
					yudeshu >>= 1;
					length += len[++lenCount];
				}
			}
			out.write(bufout);
			out.flush();
		}
		out.write(buf, 0, num);
		out.flush();
		out.close();
		in.close();
		encripting = false;
		encripted = true;
		int option = JOptionPane.showConfirmDialog(null, "任务完成，是否打开文件");
		if (option == JOptionPane.OK_OPTION) {
			Runtime.getRuntime().exec("firefox " + saved.getParent());//explorer.exe
		}
	}

}
